package ltd.flyweight.gst200;

import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.util.*;

@Slf4j
@Component
public class Gst200Parser {

    Map<String, List<byte[]>> gst200Parsers = new HashMap<>();

    Map<String, String> gst200TypeMap = new HashMap<>();

    @PostConstruct
    void init() {
        gst200TypeMap.put("00", "未定义");
        gst200TypeMap.put("01", "光栅测温");
        gst200TypeMap.put("02", "点型感温");
        gst200TypeMap.put("03", "点型感烟");
        gst200TypeMap.put("04", "报警接口");
        gst200TypeMap.put("05", "复合火焰");
        gst200TypeMap.put("06", "光束感烟");
        gst200TypeMap.put("07", "紫外火焰");
        gst200TypeMap.put("08", "线型感温");
        gst200TypeMap.put("09", "吸气感烟");
        gst200TypeMap.put("10", "复合探测");
        gst200TypeMap.put("11", "手动按钮");
        gst200TypeMap.put("12", "消防广播");
        gst200TypeMap.put("13", "讯响器");
        gst200TypeMap.put("14", "消防电话");
        gst200TypeMap.put("15", "消火栓");
        gst200TypeMap.put("16", "消火栓泵");
        gst200TypeMap.put("17", "喷淋泵");
        gst200TypeMap.put("18", "稳压泵");
        gst200TypeMap.put("19", "排烟机");
        gst200TypeMap.put("20", "送风机");
        gst200TypeMap.put("21", "新风机");
        gst200TypeMap.put("22", "防火阀");
        gst200TypeMap.put("23", "排烟阀");
        gst200TypeMap.put("24", "送风阀");
        gst200TypeMap.put("25", "电磁阀");
        gst200TypeMap.put("26", "卷帘门中");
        gst200TypeMap.put("27", "卷帘门下");
        gst200TypeMap.put("28", "防火门");
        gst200TypeMap.put("29", "压力开关");
        gst200TypeMap.put("30", "水流指示");
        gst200TypeMap.put("31", "电梯");
        gst200TypeMap.put("32", "空调机组");
        gst200TypeMap.put("33", "柴油发电");
        gst200TypeMap.put("34", "照明配电");
        gst200TypeMap.put("35", "动力配电");
        gst200TypeMap.put("36", "水幕电磁");
        gst200TypeMap.put("37", "气体启动");
        gst200TypeMap.put("38", "气体停动");
        gst200TypeMap.put("39", "从机");
        gst200TypeMap.put("40", "火灾显示盘");
        gst200TypeMap.put("41", "闸阀");
        gst200TypeMap.put("42", "干粉灭火");
        gst200TypeMap.put("43", "泡沫泵");
        gst200TypeMap.put("44", "消防电源");
        gst200TypeMap.put("45", "紧急照明");
        gst200TypeMap.put("46", "疏导指示");
        gst200TypeMap.put("47", "喷洒指示");
        gst200TypeMap.put("48", "防盗模块");
        gst200TypeMap.put("49", "信号碟阀");
        gst200TypeMap.put("50", "防排烟阀");
        gst200TypeMap.put("51", "水幕泵");
        gst200TypeMap.put("52", "层号灯");
        gst200TypeMap.put("53", "设备停动");
        gst200TypeMap.put("54", "泵故障");
        gst200TypeMap.put("55", "急启按钮");
        gst200TypeMap.put("56", "急停按钮");
        gst200TypeMap.put("57", "雨淋泵");
        gst200TypeMap.put("58", "上位机");
        gst200TypeMap.put("59", "回路");
        gst200TypeMap.put("60", "空压机");
        gst200TypeMap.put("61", "联动电源");
        gst200TypeMap.put("62", "多线制锁");
        gst200TypeMap.put("63", "部分设备");
        gst200TypeMap.put("64", "雨淋阀");
        gst200TypeMap.put("65", "感温棒");
        gst200TypeMap.put("66", "故障输出");
        gst200TypeMap.put("67", "手动允许");
        gst200TypeMap.put("68", "自动允许");
        gst200TypeMap.put("69", "可燃气体");
        gst200TypeMap.put("70", "备用指示");
        gst200TypeMap.put("71", "门灯");
        gst200TypeMap.put("72", "备用工作");
        gst200TypeMap.put("73", "设备故障");
        gst200TypeMap.put("74", "紧急求助");
        gst200TypeMap.put("75", "时钟电源");
        gst200TypeMap.put("76", "警报输出");
        gst200TypeMap.put("77", "报警传输");
        gst200TypeMap.put("78", "环路开关");
        gst200TypeMap.put("79", "未定义");
        gst200TypeMap.put("80", "未定义");
        gst200TypeMap.put("81", "消火栓");
        gst200TypeMap.put("82", "缆式感温");
        gst200TypeMap.put("83", "吸气感烟");
        gst200TypeMap.put("84", "吸气火警");
        gst200TypeMap.put("85", "吸气预警");
        gst200TypeMap.put("86", "探测器脏");
        gst200TypeMap.put("87", "多线制盘");
        gst200TypeMap.put("88", "模拟感温");
        gst200TypeMap.put("89", "漏电报警");
        gst200TypeMap.put("90", "总线");
        gst200TypeMap.put("91", "保留");
        gst200TypeMap.put("92", "保留");
        gst200TypeMap.put("93", "保留");
        gst200TypeMap.put("94", "保留");
        gst200TypeMap.put("95", "保留");
        gst200TypeMap.put("96", "保留");
        gst200TypeMap.put("97", "保留");
        gst200TypeMap.put("98", "保留");
        gst200TypeMap.put("99", "保留");
    }

    public void parse(String line) {
        String[] split = line.split("#");
        long id = Long.parseLong(split[0], 16);
        byte[] data = new byte[8];
        for (int i = 0; i < 8; i++) {
            byte b = (byte) Integer.parseInt(split[1].substring(i * 2, i * 2 + 2), 16);
            data[i] = b;
        }
        CANFrame canFrame = CANFrame.builder().id(id).data(data).line(line).build();
        boolean isAck = (id >>> 28 & 0b1) == 0;
        int code = (int) (id >>> 21 & 0b1111111);
        int mark = (int) (id >>> 18 & 0b111);
        int sequence = (int) (id & 0b11111111);
//        switch (code) {
//            case 1:
//        }
        String remarkPrefix;
        String remarkText;
        String remarkSuffix = isAck ? "应答" : "";
        switch (code) {
            case 0x6A: {
                remarkPrefix = "时间同步";
                remarkText = toBCDString(data, 0, 1) + ":" + toBCDString(data, 1, 1);
                break;
            }
            case 0x41: {
                remarkPrefix = "火警状态";
                remarkText = getAlarmEventRemarkText(data);
                break;
            }
            case 0x02: {
                remarkPrefix = "火警数据-第" + mark + "包";
                remarkText = getAlarmDataRemarkText(data);
                break;
            }
            case 0x42: {
                remarkPrefix = "启动状态";
                remarkText = getStartEventRemarkText(data);
                break;
            }
            case 0x03: {
                remarkPrefix = "启动数据-第" + mark + "包";
                remarkText = getStartDataRemarkText(data);
                break;
            }
            case 0x71: {
                remarkPrefix = "主机心跳";
                remarkText = "一级网络编号=%s".formatted(toBCDString(data, 0, 1));
                break;
            }
            case 0x61: {
                remarkPrefix = "故障状态";
                remarkText = getFaultEventRemarkText(data);
                break;
            }
            case 0x04: {
                remarkPrefix = "故障数据-第" + mark + "包";
                remarkText = getFaultDataRemarkText(data);
                break;
            }
            case 0x25: {
                remarkPrefix = "主机复位";
                remarkText = "无";
                break;
            }
            case 0x26: {
                remarkPrefix = "启动指令";
                remarkText = getStartupEventRemarkText(data);
                break;
            }
            case 0x64: {
                remarkPrefix = "停止指令";
                remarkText = getShutdownEventRemarkText(data);
                break;
            }
            case 0x28: {
                remarkPrefix = "报警启动";
                remarkText = "无";
                break;
            }
            case 0x29: {
                remarkPrefix = "报警消音";
                remarkText = "无";
                break;
            }
            case 0x2A: {
                remarkPrefix = "屏蔽";
                remarkText = getShieldEventRemarkText(data);
                break;
            }
            case 0x2B: {
                remarkPrefix = "开放";
                remarkText = getOpenEventRemarkText(data);
                break;
            }
            case 0x2E: {
                remarkPrefix = "故障消除";
                remarkText = "无";
                break;
            }
            default: {
                remarkPrefix = "未知指令";
                remarkText = "未知";
            }
        }
        log.info("{}: CODE={}, MARK={}, SEQUENCE={}, LINE={} ({}【{}】{})", isAck ? "ACK" : "ASK", String.format("%02X", code), mark, String.format("%02X", sequence), line, remarkPrefix, remarkText, remarkSuffix);
    }

    private String getStartDataRemarkText(byte[] data) {
        return getActionDataCommonRemarkText(data);
    }

    private String getFaultDataRemarkText(byte[] data) {
        return getActionDataCommonRemarkText(data);
    }

    private String getAlarmDataRemarkText(byte[] data) {
        return getActionDataCommonRemarkText(data);
    }

    private String getActionDataCommonRemarkText(byte[] data) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < data.length / 2; i++) {
            byte b1 = data[i * 2];
            byte b2 = data[i * 2 + 1];
            // 第一个字节为0代表第二个字节是ASCII码
            if (b1 == 0) {
                // 过滤掉终止字符
                if (b2 == 0x00) {
                    continue;
                }
                sb.append((char) b2);
            }
            // 第一个字节不为0代表第一第二个字节合起来是个区位码
            else {
                String hex = String.format("%02X", Integer.parseInt(String.format("%02X", b1))) + String.format("%02X", Integer.parseInt(String.format("%02X", b2)));
                // 计算国标码（区位码的十六进制 + 0x2020）
                int gbCode = Integer.parseInt(hex, 16);
                String s2 = String.format("%04X", gbCode);
                gbCode += 0x2020;
                String s3 = String.format("%04X", gbCode);
                // 计算机内码（国标码 + 0x8080）
                int internalCode = gbCode + 0x8080;
                String s4 = String.format("%04X", internalCode);
                // 将机内码拆分为两个字节
                byte[] bytes = new byte[2];
                bytes[0] = (byte) ((internalCode >> 8) & 0xFF);
                bytes[1] = (byte) (internalCode & 0xFF);

                try {
                    sb.append(new String(bytes, "GB2312").charAt(0));
                } catch (UnsupportedEncodingException e) {
                    sb.append("?");
                }
            }
        }
        // 过滤掉最后的空格再返回
        return StringUtils.stripEnd(sb.toString(), " ");
    }

    private String getOpenEventRemarkText(byte[] data) {
        return getActionEventCommonRemarkText(data);
    }

    private String getShieldEventRemarkText(byte[] data) {
        return getActionEventCommonRemarkText(data);
    }

    private String getShutdownEventRemarkText(byte[] data) {
        return getActionEventCommonRemarkText(data);
    }

    private String getStartupEventRemarkText(byte[] data) {
        return getActionEventCommonRemarkText(data);
    }

    private String getFaultEventRemarkText(byte[] data) {
        return getActionEventCommonRemarkText(data);
    }

    private String getAlarmEventRemarkText(byte[] data) {
        return getActionEventCommonRemarkText(data);
    }

    private String getStartEventRemarkText(byte[] data) {
        return getActionEventCommonRemarkText(data);
    }

    private String getActionEventCommonRemarkText(byte[] data) {
        // 二次码
        String s1 = toBCDString(data, 0, 3);
        // 类型码
        String s2 = "%02d".formatted(data[3] & 0xFF);
        // 类型名称
        String s3 = gst200TypeMap.get(s2);
        if (s3 == null) {
            s3 = "未知";
        }
        return "二次码=%s, 类型码=%s, 类型名=%s".formatted(s1, s2, s3);
    }

    public String toHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        sb.deleteCharAt(sb.length() - 1);
        return sb.toString();
    }

    public String toBCDString(byte[] bytes) {
        return toBCDString(bytes, 0, bytes.length);
    }

    public String toBCDString(byte[] bytes, int offset, int len) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < len; i++) {
            byte b = bytes[offset + i];
            sb.append(String.format("%d", b >>> 4 & 0b1111));
            sb.append(String.format("%d", b & 0b1111));
        }
        return sb.toString();
    }
}
